/*
 *  cpu_parse_display/cpu_parce.c
 *  
 *              CPU Monitor 
 *
 *    Spring Semester.  044800.    Technion.
 *   Copyright (C) 2006  Evgeny Shulman  and Elizabeth Shtrom 
 */


#include "cpu_display.h"

#ifdef __KERNEL__

#include <linux/memory.h>
#include <linux/string.h>
#include <linux/errno.h>

#else
     /* This one for user space */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#endif

#include "cpu_parse.h"
#include "../cpu_consts.h"
#include "../cpu_common.h"



static inline int disp_pte_user(disp_pte_t pte)		{ return (pte).pte_low & DISP_PAGE_USER; }
static inline int disp_pte_read(disp_pte_t pte)		{ return (pte).pte_low & DISP_PAGE_USER; }
static inline int disp_pte_dirty(disp_pte_t pte)	{ return (pte).pte_low & DISP_PAGE_DIRTY; }
static inline int disp_pte_young(disp_pte_t pte)	{ return (pte).pte_low & DISP_PAGE_ACCESSED; }
static inline int disp_pte_write(disp_pte_t pte)	{ return (pte).pte_low & DISP_PAGE_RW; }
static inline int disp_pte_huge(disp_pte_t pte)		{ return ((pte).pte_low & DISP__LARGE_PTE) == DISP__LARGE_PTE; }
static inline int disp_pte_exec(disp_pte_t pte)   { return disp_pte_user(pte);  }

static char *gate_desc_types[] = {
	"invalid", "tss-avlb", "ldt", "tss-busy",
	"callgate", "taskgate", "intgate", "trapgate",
	"invalid", "tss32-avlb", "invalid", "tss32-busy",
	"callgate32", "invalid", "intgate32", "trapgate32",
	NULL };

	
//static char *set_eflags[] = {
//	"carry", NULL, "parity",  NULL, "adjust",  NULL, "zero", "sign",
//	"trace", "intr-on", "dir", "overflow",  NULL, NULL, "nestedtask", NULL,
//	"resume", "vm", "align", "vif", "vip", "id", NULL, NULL,
//	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};	
//	
//	



void init_desc  ( parsed_desc_s *s) { 
	*(s->index)     = '\0'; 
	*(s->status)   = '\0'; 
	*(s->type)     = '\0'; 
	*(s->base)     = '\0'; 
	*(s->limit)    = '\0';
	*(s->dpl  )    = '\0';
	*(s->sel )     = '\0';
	*(s->off )     = '\0';
	*(s->wc  )     = '\0';
	*(s->mode )    = '\0';
	*(s->type_1 )  = '\0';
	*(s->type_2   )= '\0';
	*(s->db    )   = '\0';
	*(s->entry    )= '\0';
	*(s->g        )= '\0';
} 

void init_page  ( parsed_page_s *s) { 
	*(s->index   )  = '\0';
	*(s->user    )  = '\0';
	*(s->mode    )  = '\0';
	*(s->dirty   )  = '\0';
	*(s->accessed)  = '\0';
	*(s->avail     )  = '\0';
	*(s->sel     )  = '\0';
	*(s->off     )  = '\0';
	*(s->global  )  = '\0';
	*(s->pse     )  = '\0';
	*(s->pwt     )  = '\0';
	*(s->pcd     )  = '\0';
	*(s->status  )  = '\0';
	*(s->frame   )  = '\0';
	*(s->swp_offset)= '\0';
	*(s->swp_type ) = '\0';
	*(s->entry   )  = '\0';
}              


void init_Xgt  ( parsed_Xgt_s *s) { 
	*(s->address) = '\0';
	*(s->size   ) = '\0';
} 

void init_regs  ( parsed_regs_s *s) { 
	*(s->cr0	)  = '\0'; 
	*(s->cr3    )  = '\0'; 
	*(s->cr4    )  = '\0'; 
	*(s->cr2    )  = '\0'; 
	*(s->dr0    )  = '\0'; 
	*(s->dr1    )  = '\0'; 
	*(s->dr2    )  = '\0'; 
	*(s->dr3    )  = '\0'; 
	*(s->dr4    )  = '\0'; 
	*(s->dr5    )  = '\0'; 
	*(s->dr6    )  = '\0'; 
	*(s->dr7    )  = '\0'; 
	*(s->cs     )  = '\0'; 
	*(s->ss		  )  = '\0'; 
	*(s->es     )  = '\0'; 
	*(s->ds     )  = '\0'; 
	*(s->fs     )  = '\0'; 
	*(s->gs     )  = '\0'; 
	*(s->ldtr   )  = '\0'; 
  *(s->ldtr   )  = '\0'; 
  init_Xgt ( &(s->gdtr));
  init_Xgt ( &(s->idtr));
}
    
void init_tss  ( parsed_tss_s *s) { 
	
	*(s->iobase	   )  = '\0'; 
	*(s->ldtsegsel )  = '\0'; 
	*(s->cs        )  = '\0'; 
	*(s->ss      	 )  = '\0'; 
	*(s->es        )  = '\0'; 
	*(s->ds        )  = '\0'; 
	*(s->fs        )  = '\0'; 
	*(s->gs        )  = '\0'; 
	*(s->edi       )  = '\0'; 
	*(s->esi       )  = '\0'; 
	*(s->ebp       )  = '\0'; 
	*(s->esp       )  = '\0'; 
	*(s->ebx       )  = '\0'; 
	*(s->edx	   	 )  = '\0'; 
	*(s->ecx       )  = '\0'; 
	*(s->eax       )  = '\0'; 
	*(s->eflags    )  = '\0'; 
	*(s->eip       )  = '\0'; 
	*(s->cr3       )  = '\0'; 
	*(s->ss2       )  = '\0'; 
  *(s->esp2      )  = '\0';
  *(s->ss1       )  = '\0'; 
  *(s->esp1      )  = '\0';
  *(s->ss0       )  = '\0'; 
  *(s->esp0      )  = '\0'; 
  *(s->prevtask  )  = '\0';
}       

#ifdef __KERNEL__ 	
  int  parse_tss(parsed_tss_s *s , struct tss_struct  *t)
#else	
  int  parse_tss(parsed_tss_s *s , cm_tss_struct *t)
#endif
{
	init_tss(s);
	if (t) {
	
			snprintf(s->iobase    , 12, "0x%04x", t->io_bitmap_base );
			snprintf(s->ldtsegsel , 12, "0x%04x", t->ldt            );
			snprintf(s->gs , 12, "0x%04x", t->gs );
			snprintf(s->fs , 12, "0x%04x", t->fs );
			snprintf(s->ds , 12, "0x%04x", t->ds );
			snprintf(s->ss , 12, "0x%04x", t->ss );
			snprintf(s->cs , 12, "0x%04x", t->cs );
			snprintf(s->es , 12, "0x%04x", t->es );
			
			snprintf(s->edi , 12, "0x%08lx", t->edi );
			snprintf(s->esi , 12, "0x%08lx", t->esi );
			snprintf(s->ebp , 12, "0x%08lx", t->ebp );
			snprintf(s->esp , 12, "0x%08lx", t->esp );
			snprintf(s->ebx , 12, "0x%08lx", t->ebx );
			snprintf(s->edx , 12, "0x%08lx", t->edx );
			snprintf(s->ecx , 12, "0x%08lx", t->ecx );
			snprintf(s->eax , 12, "0x%08lx", t->eax );
			
			snprintf(s->eflags , 12, "0x%08lx", t->eflags );
			snprintf(s->eip    , 12, "0x%08lx", t->eip    );
			snprintf(s->cr3    , 12, "0x%08lx", t->__cr3  );
			
			snprintf(s->ss2      , 12, "0x%04x" , t->ss2       );
			snprintf(s->esp2     , 12, "0x%08lx", t->esp2      );
			snprintf(s->ss1      , 12, "0x%04x" , t->ss1       );
			snprintf(s->esp1     , 12, "0x%08lx", t->esp1      );
			snprintf(s->ss0      , 12, "0x%04x" , t->ss0       );
			snprintf(s->esp0     , 12, "0x%08lx", t->esp0      );
			snprintf(s->prevtask , 12, "0x%04x" , t->back_link );
	}
			return 0;
}



int  parse_regs      (regs_s  *r, parsed_regs_s *s){
	snprintf(s->cr0,20, "0x%08lx", r->cr0) ;
	snprintf(s->cr2,20, "0x%08lx", r->cr2);
	snprintf(s->cr3,20, "0x%08lx", r->cr3);
	snprintf(s->cr4,20, "0x%08lx", r->cr4);
	snprintf(s->dr0,20, "0x%08lx", r->dr0);
	snprintf(s->dr1,20, "0x%08lx", r->dr1);
	snprintf(s->dr2,20, "0x%08lx", r->dr2);
	snprintf(s->dr3,20, "0x%08lx", r->dr3);	
	snprintf(s->dr4,20, "0x%08lx", r->dr4);
	snprintf(s->dr5,20, "0x%08lx", r->dr5);
	snprintf(s->dr6,20, "0x%08lx", r->dr6);
	snprintf(s->dr7,20, "0x%08lx", r->dr7);
	
	
	snprintf(s->cs,20, "0x%04hx", r->cs);
	snprintf(s->ss,20, "0x%04hx", r->ss);
	snprintf(s->es,20, "0x%04hx", r->es);
	snprintf(s->ds,20, "0x%04hx", r->ds);	
	snprintf(s->fs,20, "0x%04hx", r->fs);
	snprintf(s->gs,20, "0x%04hx", r->gs);
	
	snprintf(s->ldtr,20, "%04x:%08lx:%04x", r->ldtr.SegSel, r->ldtr.Base, r->ldtr.SegLimit);
	snprintf(s->tr, 20,  "%04x:%08lx:%04x", r->tr.SegSel, r->tr.Base, r->tr.SegLimit);
	
	parse_Xgt ( &(r->gdtr) , &(s->gdtr));
	parse_Xgt ( &(r->idtr) , &(s->idtr) );
	return 0 ;
}	


int  parse_idt      (cm_gate_desc_t *idt, parsed_desc_s *out, int vec,  int count ){
	parsed_desc_s *s;
	if ( ! CHECK_INDEX(vec, IDT_ENTRIES) ) 	 return -INDEX_INVALID;
	FIX_COUNTER( vec, count, IDT_ENTRIES, IDT_ENTRIES);
	
//	PDEBUG("\nIDT:\n");

	for ( s = out + vec; count ; count--, s++) {
		cm_gate_desc_t *d = &(idt[vec]);
		init_desc (s);
		snprintf(s->entry ,20, "0x%016llx", *(( unsigned long long *)d) );
		snprintf(s->index ,10, "0x%03x", vec++);
		if (!d->present) {
			snprintf(s->status ,20,"not present");
			continue;
		}
		
		if (d->seg) {
			snprintf(s->status ,20,"invalid"); 
			continue;
		}
		 snprintf(s->status ,20,"present");
		 parse_gate_desc(d, s);
	}
	return 0;
}

int  parse_idt_stats(im_irq_stat_s *im_irq_stat_data){
	int i ;
	for (  i =  0 ; i <IDT_ENTRIES ; i ++){
		if ( im_irq_stat_data->irqs[i] ) 
		   PDEBUG_DISP(" %d   -> %d ", i, im_irq_stat_data->irqs[i]) ;
	} 
	return 0;
}


int parse_gdt      (cm_desc_t *gdt,parsed_desc_s *out,  int sel,	int count)
{
	parsed_desc_s *s;
	if ( ! CHECK_INDEX(sel, GDT_ENTRIES) ) 	 return -INDEX_INVALID;
	FIX_COUNTER( sel, count, GDT_ENTRIES, GDT_ENTRIES);
	
	//PDEBUG("\n parse_gdt:\n");
	for (s = out + sel ;count; count--, s++) {
		cm_desc_t * d = &gdt[sel];
		init_desc (s);
		snprintf(s->entry ,20, "0x%016llx", *(( unsigned long long *)d) );
		snprintf(s->index ,10,"0x%03x", sel++);
		
		if (!d->present) { 
			snprintf(s->status ,20,"not present");
			continue;
		}
		
		snprintf(s->status ,20,"present");
		snprintf(s->g ,12,"%c",(d->g)?'1':' ');
			
		if (d->seg)
			 parse_seg_desc(d, s);
		else
			 parse_gate_desc( (cm_gate_desc_t *)d, s );
	}
	return 0;
}
	
	
int  parse_ldt  (cm_desc_t *ldt, parsed_desc_s *out,  int sel,	int count  ) {
	parsed_desc_s *s;
	
	if ( ! CHECK_INDEX(sel, LDT_ENTRIES) ) 	 return -INDEX_INVALID;
	FIX_COUNTER( sel, count, LDT_ENTRIES, LDT_ENTRIES);
	
	
	//PDEBUG("\nLDT:\n");
	for (s = out + sel ;count; count--, s++) {
		cm_desc_t * d = &ldt[sel];
		init_desc (s);
		snprintf(s->entry ,20, "0x%016llx", *(( unsigned long long *)d) );
		snprintf(s->index ,10,"0x%03x", sel++);
		
		if (!d->present) { 
			snprintf(s->status ,20,"not present");
			continue;
		}

		snprintf(s->status ,20,"present");
		snprintf(s->g ,12,"%c",(d->g)?'1':' ');
	
		if (d->seg)
			 parse_seg_desc(d, s);
		else
			 parse_gate_desc((cm_gate_desc_t *)d,   s );
		}
	return 0;
}

	
int  parse_gate_desc(cm_gate_desc_t * d, parsed_desc_s *s)
{
	#ifdef __KERNEL__ 	
		  parsed_tss_s p_tss;
			struct tss_struct *tss;
	#endif
	
	snprintf(s->type ,20,"%-11s ", gate_desc_types[d->type]);

	switch(d->type) {
	case CM_SYS_DESC_TYPE_LDT:
		snprintf(s->base  ,20,"0x%8.8lx",	CM_SEG_DESC_BASE(((cm_desc_t *)d)));
		snprintf(s->limit ,20,"0x%08lx",	CM_SEG_DESC_LIMIT(((cm_desc_t *)d)));
		snprintf(s->dpl   ,20,"%d",			 d->dpl);
		break;
	case CM_SYS_DESC_TYPE_TSS32:
	case CM_SYS_DESC_TYPE_TSS32B:
	{
		snprintf(s->base  ,20,"0x%8.8lx",	CM_SEG_DESC_BASE(((cm_desc_t *)d)));
		snprintf(s->limit ,20,"0x%08lx",	CM_SEG_DESC_LIMIT(((cm_desc_t *)d)));
		snprintf(s->dpl   ,20,"%d",			 d->dpl);

#ifdef __KERNEL__ 	
		tss = ( struct tss_struct  *)CM_SEG_DESC_BASE(((cm_desc_t *)d));
		parse_tss(&p_tss,tss);
#endif
		break;
		
	}
	case CM_SYS_DESC_TYPE_CALLG:
	case CM_SYS_DESC_TYPE_CALLG32:
		snprintf(s->sel  ,20,"%d",  		d->sel                );
		snprintf(s->off  ,20,"0x%8.8lx",	CM_SYS_DESC_OFFSET(d));
		snprintf(s->dpl  ,20,"%d",			d->dpl);
		snprintf(s->wc   ,20,"%d",			CM_SYS_DESC_CALLG_COUNT(d));
		break;
	default:
		snprintf(s->sel  ,20,"%d",  	d->sel                );
		snprintf(s->off  ,20,"0x%8.8lx",	CM_SYS_DESC_OFFSET(d));
		snprintf(s->dpl  ,20,"%d",			d->dpl);
		break;
	}

	return 0;
}


int  parse_seg_desc(cm_desc_t * d, parsed_desc_s *s)
{
	unsigned char type = d->type;

	if (type & CM_SEG_DESC_TYPE_CODE) {
		snprintf(s->type  ,20,"%-7s"     ,"code"); 
		snprintf(s->base  ,20,"0x%8.8lx" , CM_SEG_DESC_BASE(d));
		snprintf(s->limit ,20,"0x%08lx" , CM_SEG_DESC_LIMIT(d) );
		snprintf(s->dpl   ,20,"%d"       , d->dpl );
		snprintf(s->mode, 20,"%c%c%c"    ,    (type & CM_SEG_DESC_TYPE_CODE_R)?'r':'-',
			                               '-', 
			                               'x');
		snprintf(s->db,    20,"%s",	d->db ? "32b" : "16b" );
		snprintf(s->type_1,20,"%s", (type & CM_SEG_DESC_TYPE_A)?"ac":"" );
		snprintf(s->type_2,20,"%s", (type & CM_SEG_DESC_TYPE_CODE_C)?"conf":"");
	}
	else {
		snprintf(s->type  ,20,"%-7s"     ,"data"); 
		snprintf(s->base  ,20,"0x%8.8lx" , CM_SEG_DESC_BASE(d));
		snprintf(s->limit ,20,"0x%08lx" , CM_SEG_DESC_LIMIT(d) );
		snprintf(s->dpl   ,20,"%d"       , d->dpl );
		snprintf(s->mode, 20,"%c%c%c"    ,   'r',
			(type & CM_SEG_DESC_TYPE_DATA_W)?'w':'-',
			'-');
		snprintf(s->db,    20,"%s",	d->db ? "32b" : "16b" );
		snprintf(s->type_1,20,"%s", (type & CM_SEG_DESC_TYPE_A)?"ac":"" );
		snprintf(s->type_2,20,"%s", (type & CM_SEG_DESC_TYPE_DATA_D)?"down":"");
	}

	return 0;
}



//void  parse_eflags(unsigned long ef)
//{
//	int i, iopl;
//	PDEBUG("eflags = 0x%08lx ", ef);
//	for (i = 0; i < 22; i++) {
//		if (test_bit(i, &ef) && set_eflags[i])
//			PDEBUG("%s ", set_eflags[i]);
//}


int  parse_Xgt( struct Xgt_desc_struct *r, parsed_Xgt_s *s){
	init_Xgt (s);
	snprintf(s->address ,15,"%p",  (void*)  r->address);
	snprintf(s->size   ,15,"Ox%hx",   r->size);
	return 0;
}




int  parse_pgdir( void * pgdir,parsed_page_s *out , int index , int count )
{
	unsigned long entry;
	int i;
	parsed_page_s *s     = out +  index;
	disp_pgd_t * pgdir_p = (disp_pgd_t *) pgdir + index; 

	if ( ! CHECK_INDEX(index, PGD_TABLE_ENTRIES) ) 	 return -INDEX_INVALID;
	FIX_COUNTER( index, count, PGD_TABLE_ENTRIES, PGD_TABLE_ENTRIES);
	
	for (i = 0; i < count; i++, pgdir_p++, s++) {
		init_page (s);
		
		entry = disp_pgd_val(*pgdir_p);
		
		snprintf(s->index ,10,"0x%03x",i+index);
		if (disp_pgd_none(*pgdir_p)) {
				snprintf(s->status ,20,"not present");
		} else {
			
		if (entry) snprintf(s->entry,20,"0x%08lx",entry);
	  snprintf(s->status  ,20,"present");
	  snprintf(s->avail   ,10,"%1lx" ,( ( entry & DISP_PAGE_AVAIL )  >> DISP_AVAIL_SHIFT ) );
		snprintf(s->frame   ,20, "%05lx", (entry >> DISP_PAGE_SHIFT) ) ;
		snprintf(s->mode    ,10, "%s",  (entry & DISP_PAGE_RW)?"rw":"ro"    );
		snprintf(s->user    ,10, "%c",  (entry & DISP_PAGE_USER)?'u':'s'   );
		snprintf(s->accessed,10, "%c",   (entry & DISP_PAGE_ACCESSED)?'a':' ');
		snprintf(s->pse     ,10,"%s",  (entry & DISP_PAGE_PSE)?"4M":"4K" );
		snprintf(s->global  ,10,"%c" , (entry & DISP_PAGE_GLOBAL)? 'g':' ');
		snprintf(s->pwt     ,10,"%s",  (entry & DISP_PAGE_PWT)? "wt":"wb" ); 	
		snprintf(s->pcd     ,10,"%s",  (entry & DISP_PAGE_PCD)? "cd":"  " ); 
				
		}
	}
	return 0;
} 

int  parse_ptable( void * ptable ,parsed_page_s *out , int index , int count )
{
	unsigned long entry;
	int i;
	parsed_page_s *s     = out +  index;
	disp_pte_t * ptable_p  = (disp_pte_t *)ptable  + index;
	
	if ( ! CHECK_INDEX(index, PT_TABLE_ENTRIES) ) 	 return -INDEX_INVALID;
	FIX_COUNTER( index, count, PT_TABLE_ENTRIES, PT_TABLE_ENTRIES);
	
  	//PDEBUG("parse_ptable:\n");
 	for (i = 0; i < count; i++, ptable_p++, s++) {
 		init_page (s);
 		
		snprintf(s->index ,10,"0x%03x",i+index);
		entry =disp_pte_val(*ptable_p);
		if (entry)
			snprintf(s->entry,20,"0x%08lx",entry);
		if (disp_pte_none(*ptable_p)) {
			snprintf(s->status ,20,"not present");
		} else if (!disp_pte_present(*ptable_p)) {
				snprintf(s->status     ,20,"page swapped out");
				snprintf(s->swp_offset ,20,"%08lx", 
				                       disp__swp_offset(disp__pte_to_swp_entry(*ptable_p)));
				snprintf(s->swp_type ,20,"%lx",
				                        disp__swp_type(disp__pte_to_swp_entry(*ptable_p)));
		} else {
			snprintf(s->status ,20,"present");
			snprintf(s->frame  ,20, "%05lx",(entry >> DISP_PAGE_SHIFT) ); 
			snprintf(s->mode ,10,"%c%c%c"	,
				(disp_pte_read (*ptable_p))? 'r':'-',
				(disp_pte_write(*ptable_p))? 'w':'-', 
				(disp_pte_exec (*ptable_p))? 'x':'-' );  
					
	    snprintf(s->avail   ,10,"%1lx" ,( ( entry & DISP_PAGE_AVAIL )  >> DISP_AVAIL_SHIFT ) );
	    snprintf(s->mode    ,10, "%s",  (entry & DISP_PAGE_RW)?"rw":"ro"    );
			snprintf(s->dirty   ,10,"%c" ,(disp_pte_dirty(*ptable_p))? 'd':' ');	
			snprintf(s->accessed,10,"%c" ,(disp_pte_young(*ptable_p))? 'a':' ');  	
			snprintf(s->user    ,3, "%c", (entry & DISP_PAGE_USER)? 'u':'s'  );  		
			snprintf(s->global  ,10,"%c" , (entry & DISP_PAGE_GLOBAL)? 'g':' ');
			snprintf(s->pwt     ,10,"%s" ,     (entry & DISP_PAGE_PWT)? "wt":"wb" ); 	
			snprintf(s->pcd     ,10,"%s" ,     (entry & DISP_PAGE_PCD)? "cd":"  " ); 
		}
	}  
 return 0;
}


